#include "../lib/routingtable.hpp"

#include <tinyxml.h>
#include <coreutil.hpp>
#include <string>

using namespace kport;

RoutingTable* RoutingTable::instance_ = NULL;

RoutingTable::RoutingTable()
{
    map_ = new RoutingMap();
	ready_ = false;
}

RoutingTable::RoutingTable(std::string config_xml)
{
    map_ = new RoutingMap();
	loadTablePrivate_(config_xml);
}

void RoutingTable::loadTablePrivate_(std::string config_xml) throw(pland::Exeption)
{
	using namespace pland;
    boost::unique_lock< boost::shared_mutex > lock(access_);
    RoutingMap::iterator iter;
	for(iter = map_->begin(); iter != map_->end(); iter++)
	{
		delete iter->second.second;
	}
	map_->clear();
	if(!util_fexists(config_xml))
	{
		utill_log(std::string(__FILE__) + std::string("No config file ") + config_xml);
		throw Exeption("No config file " + config_xml, PEXP_BAD_CONFIG | PEXP_FATAL);
	}
	TiXmlDocument doc(config_xml);

	if(!doc.LoadFile())
	{
		utill_log(std::string(__FILE__) + std::string("Bad config file ") + config_xml);
		throw Exeption("Bad config file " + config_xml, PEXP_BAD_CONFIG | PEXP_FATAL);
	}
	ready_ = true;
	TiXmlNode *root = doc.RootElement();
	TiXmlNode *route = root->FirstChild("route");
	while(route != NULL)
	{
		if(route->FirstChild("port") == NULL || route->FirstChild("net") == NULL || route->FirstChild("ip") == NULL)
		{
			utill_log(std::string(__FILE__) + " routing table: " +config_xml +  " mallformed route");
			route = root->IterateChildren("route", route);
			continue;
		}
		std::string dest = "";
		if(route->FirstChild("dest") != NULL)
		{
			dest = route->FirstChild("dest")->FirstChild()->Value();
		}
		std::string port_s = route->FirstChild("port")->FirstChild()->Value();
		std::string net = route->FirstChild("net")->FirstChild()->Value();
		std::string ip_s = route->FirstChild("ip")->FirstChild()->Value();
        outGoingNets_.push_back(net);
        utill_log(std::string(__FILE__) + "; loaded route for net " + net);
		struct sockaddr_in * serv_addr = new struct sockaddr_in;
		struct hostent *server = gethostbyname(ip_s.c_str());
		if(server == NULL)
		{
			utill_log(std::string(__FILE__) + "; routing table: unknown host " + ip_s);
			route = root->IterateChildren("route", route);
			continue;
		}
		int port = atoi(port_s.c_str());

		bzero((char *) serv_addr, sizeof(sockaddr_in));
		serv_addr->sin_family = AF_INET;
		bcopy((char *)server->h_addr,
			  (char *)&(serv_addr->sin_addr.s_addr),
			  server->h_length);
		serv_addr->sin_port = htons(port);
		Route r;
		r.first = dest;
		r.second = serv_addr;
		map_->insert(std::make_pair(net, r));
		route = root->IterateChildren("route", route);
	}
}

RoutingTable::~RoutingTable()
{
    RoutingMap::iterator iter;
	for(iter = map_->begin(); iter != map_->end(); iter++)
	{
		delete iter->second.second;
	}
	if(ready_)
		delete map_;
}

RoutingTable::Route RoutingTable::route(std::string net) throw(pland::Exeption)
{
	using namespace pland;
	boost::shared_lock< boost::shared_mutex > lock(access_);
	if(ready_)
	{
		if(map_->find(net) != map_->end())
			return map_->at(net);
		else
		{
			utill_log(std::string(__FILE__) + " Could not route net " + net);
			throw Exeption("Could not route net " + net, PEXP_BAD_CONFIG | PEXP_FATAL);
		}
	}
	else
	{
		utill_log(std::string(__FILE__) + " Routing table not loaded.");
		throw Exeption("Routing table not loaded", PEXP_USER | PEXP_FATAL);
	}
	return std::make_pair("", (struct sockaddr_in *)NULL);
}

std::vector <std::string> RoutingTable::outGoingNets()
{
    return outGoingNets_;
}

RoutingTable* RoutingTable::loadTable(std::string config_xml) throw(pland::Exeption)
{
	if(instance_ == NULL)
	{
		instance_ = new RoutingTable(config_xml);
	}
	else
		instance_->loadTablePrivate_(config_xml);
	return instance_;
}

RoutingTable* RoutingTable::getInstance()
{
    if(instance_ == NULL)
    {
        instance_ = new RoutingTable();
    }
	return instance_;
}
